リンクを置換するPopup Menu
置換したいリンクのページに飛ぶ必要がない
中身のあるページのリンクも置換できる
dependencies
code:script.ts
import {
patch, replaceLinks,
import type {
Scrapbox
declare const scrapbox: Scrapbox;
scrapbox.PopupMenu.addButton({
title: (text) => getLink(text) ? "update links" : "",
onClick: (text) => {
const link = getLink(text);
if (!link) return;
const newLink = window.prompt(Replace "${link}" to, link)
?.replace?.(/[\\\n]/g, ' ') ?? ""; if (newLink === "") return;
const project = scrapbox.Project.name;
Promise.all([
replaceLinks(project, link, newLink),
patch(project, link, (lines_, { persistent }) => {
const lines = lines_.map((line) => line.text);
}),
]);
}
});
function getLink(text: string): string | undefined {
}
async function replaceTitle(project: string, from: string, to: string): Promise<void> {
}
code:script.js
async function P(e){let t="https://scrapbox.io/api/users/me",n=await fetch(t,e?.sid?{headers:{Cookie:y(e.sid)}}:void 0);if(!n.ok)throw E("UnexpectedError",Unexpected error has occuerd when fetching "${t}");return await n.json()}var y=e=>connect.sid=${e};async function H(e){return window.__csrf?window.__csrf:(await P(e?{sid:e}:void 0)).csrfToken}function se(e){return e!=null}function ie(e){return se(e)?(e.name===void 0||typeof e.name=="string")&&typeof e.message=="string":!1}function k(e){try{let t=typeof e=="string"?JSON.parse(e):e;return ie(t)?t:!1}catch(t){if(t instanceof SyntaxError)return!1;throw t}}function E(e,t){let n=new Error;return n.name=e,n.message=t,n}var T=e=>e.replaceAll(" ","_").toLowerCase();var R=e=>...e.map((t,n)=>t===" "?"_":!ae.includes(t)||n===e.length-1&&ce.includes(t)?encodeURIComponent(t):t).join(""),ae='@$&+=:;",',ce=':;",';async function A(e,t,n){let o=https://scrapbox.io/api/pages/${e}/${R(t)}?followRename=${n?.followRename??!0},r=await fetch(o,n?.sid?{headers:{Cookie:y(n.sid)}}:void 0);if(!r.ok){let i=k(await r.text());if(!i)throw E("UnexpectedError",Unexpected error has occuerd when fetching "${o}");return{ok:!1,value:i}}let s=await r.json();return{ok:!0,value:s}}async function U(e,t){let n=https://scrapbox.io/api/projects/${e},o=await fetch(n,t?.sid?{headers:{Cookie:y(t.sid)}}:void 0);if(!o.ok){let s=k(await o.json());if(!s)throw E("UnexpectedError",Unexpected error has occuerd when fetching "${n}");return{ok:!1,value:s}}let r=await o.json();return{ok:!0,value:r}}async function _(e,t,n,o){let r=https://scrapbox.io/api/pages/${e}/replace/links,s=o?.sid,i=o?.csrf??await H(s),a=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json;charset=utf-8","X-CSRF-TOKEN":i,...s?{Cookie:y(s)}:{}},body:JSON.stringify({from:t,to:n})});if(!a.ok){let d=k(await a.text());if(!d)throw E("UnexpectedError",Unexpected error has occuerd when fetching "${r}");return{ok:!1,value:d}}let{message:p}=await a.json();return{ok:!0,value:parseInt(p.match(/\d+/)?.0??"0")}}var fe="4.2.0";async function L(){let t=(await ge())("https://scrapbox.io",{reconnectionDelay:5e3,transports:["websocket"]});return await new Promise((n,o)=>{let r=s=>o(s);t.once("connect",()=>{t.off("disconnect",r),n()}),t.once("disconnect",r)}),t}function ge(){let e=https://cdnjs.cloudflare.com/ajax/libs/socket.io/${fe}/socket.io.min.js;if(document.querySelector(script[src="${e}"]))return Promise.resolve(window.io);let t=document.createElement("script");return t.src=e,new Promise((n,o)=>{t.onload=()=>n(window.io),t.onerror=r=>o(r),document.head.append(t)})}function I(e,t=9e4){function n(r,s){let i;return new Promise((a,p)=>{let d=c=>{clearTimeout(i),p(new Error(c))};e.emit(r,s,c=>{clearTimeout(i),e.off("disconnect",d),c.error&&p(new Error(JSON.stringify(c.error))),"data"in c?a(c?.data):a(void 0)}),i=setTimeout(()=>{e.off("disconnect",d),p(new Error(Timeout: exceeded ${t}ms))},t),e.once("disconnect",d)})}async function*o(...r){let s,i=()=>new Promise(p=>s=p),a=p=>{s?.(p)};for(let p of r)e.on(p,a);try{for(;;)yield await i()}finally{for(let p of r)e.off(p,a)}}return{request:n,response:o}}var D;async function S(){if(D!==void 0)return D;let e=await P();if(e.isGuest)throw new Error("this script can only be executed by Logged in users");return D=e.id,D}var K=new Map;async function O(e){let t=K.get(e);if(t!==void 0)return t;let n=await U(e);if(!n.ok){let{name:r,message:s}=n.value;throw new Error(${r} ${s})}let{id:o}=n.value;return K.set(e,o),o}function q(e){return e.padStart(8,"0")}function z(e){let t=Math.floor(new Date().getTime()/1e3).toString(16),n=Math.floor(16777214*Math.random()).toString(16);return${q(t).slice(-8)}${e.slice(-6)}0000${q(n)}}function W(e,t){let n=e.length>t.length,o=n?t:e,r=n?e:t,s=o.length+1,i=o.length+r.length+3,a=new Array(i);a.fill(-1);let p=[];function d(u,h,b){let w=Math.max(h,b),N=w-u;for(;N<o.length&&w<r.length&&oN===rw;)++N,++w;return au+s=p.length,p.push([{x:N,y:w},au+(h>b?-1:1)+s]),w}let c=new Array(i);c.fill(-1);let g=-1,m=r.length-o.length;do{++g;for(let u=-g;u<=m-1;++u)cu+s=d(u,cu-1+s+1,cu+1+s);for(let u=m+g;u>=m+1;--u)cu+s=d(u,cu-1+s+1,cu+1+s);cm+s=d(m,cm-1+s+1,cm+1+s)}while(cm+s!==r.length);let C=[],x=am+s;for(;x!==-1;)C.push(px0),x=px1;return{from:e,to:t,editDistance:m+g*2,buildSES:function*(){let u=0,h=0;for(let{x:b,y:w}of he(C))for(;u<b||h<w;)w-b>h-u?(yield{value:rh,type:n?"deleted":"added"},++h):w-b<h-u?(yield{value:ou,type:n?"added":"deleted"},++u):(yield{value:ou,type:"common"},++u,++h)}}}function*Q(e){let t=[],n=[];function*o(){if(t.length>n.length){for(let r=0;r<n.length;r++)yield X(tr,nr);for(let r=n.length;r<t.length;r++)yield tr}else{for(let r=0;r<t.length;r++)yield X(tr,nr);for(let r=t.length;r<n.length;r++)yield nr}t=[],n=[]}for(let r of e)switch(r.type){case"added":t.push(r);break;case"deleted":n.push(r);break;case"common":yield*o(),yield r;break}yield*o()}function X(e,t){return{value:e.value,oldValue:t.value,type:"replaced"}}function*he(e){for(let t=e.length-1;t>=0;t--)yield et}function*Y(e,t,{userId:n}){let{buildSES:o}=W(e.map(({text:i})=>i),t),r=0,s=e0.id;for(let i of Q(o())){switch(i.type){case"added":yield{_insert:s,lines:{id:z(n),text:i.value}};continue;case"deleted":yield{_delete:s,lines:-1};break;case"replaced":yield{_update:s,lines:{text:i.value}};break}r++,s=er?.id??"_end"}}var xe=e=>({type:"title",text:e.rows0.text}),we=e=>{let{rows:t,...n}=e,{indent:o=0,text:r=""}=t??{},s=r.replace(/^\s*code:/,"");return{indent:o,type:"codeBlock",fileName:s,content:n.map(i=>i.text.substring(o+1)).join(` )}},l=(e,{parseOnNested:t,parseOnQuoted:n,patterns:o})=>(r,s,i)=>{var a,p,d,c,g,m;if(!t&&s.nested)return(a=i==null?void 0:i())!==null&&a!==void 0?a:[];if(!n&&s.quoted)return(p=i==null?void 0:i())!==null&&p!==void 0?p:[];for(let C of o){let x=C.exec(r);if(x===null)continue;let u=r.substring(0,x.index),h=r.substring(x.index+((c=(d=x[0])===null||d===void 0?void 0:d.length)!==null&&c!==void 0?c:0)),b=e((g=x[0])!==null&&g!==void 0?g:"",s);return[...v(u,s),...b,...v(h,s)]}return(m=i==null?void 0:i())!==null&&m!==void 0?m:[]},f=e=>[{type:"plain",raw:e,text:e}],ye=l(f,{parseOnNested:!0,parseOnQuoted:!0,patterns:[/^()(.*)()$/]}),Ee=/^>.*$/,be=(e,t)=>t.context==="table"?f(e,t):[{type:"quote",raw:e,nodes:v(e.substring(1),{...t,quoted:!0})}],ke=l(be,{parseOnNested:!1,parseOnQuoted:!1,patterns:[Ee]}),ve=/^\? .+$/,Te=(e,t)=>t.context==="table"?f(e,t):[{type:"helpfeel",raw:e,text:e.substring(2)}],Le=l(Te,{parseOnNested:!1,parseOnQuoted:!1,patterns:[ve]}),Ie=/\[\[https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)\]\]/i,Me=/\[\[https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}\]\]/,Ce=(e,t)=>{if(t.context==="table")return f(e,t);let n=e.substring(2,e.length-2),o=/^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}$/.test(n);return[{type:"strongImage",raw:e,src:o?${n}/thumb/1000:n}]},Ne=l(Ce,{parseOnNested:!1,parseOnQuoted:!0,patterns:[Ie,Me]}),Pe=/\[[^[\]]*\.icon(?:\*[1-9]\d*)?\]/;function G(e){return(t,n)=>{if(e==="strongIcon"&&n.context==="table")return f(t,n);let o=e==="icon"?t.substring(1,t.length-1):t.substring(2,t.length-2),r=o.lastIndexOf(".icon"),s=o.substring(0,r),i=s.startsWith("/")?"root":"relative",a=o.substring(r+5,o.length),p=a.startsWith("*")?parseInt(a.substring(1),10):1;return new Array(p).fill({}).map(()=>({path:s,pathType:i,type:e,raw:t}))}}var De=G("icon"),Oe=l(De,{parseOnNested:!1,parseOnQuoted:!0,patterns:[Pe]}),He=/\[\[[^[\]]*\.icon(?:\*\d+)?\]\]/,Re=G("strongIcon"),Se=l(Re,{parseOnNested:!1,parseOnQuoted:!0,patterns:[He]}),je=/\[\[(?:[^[]|\[[^[]).*?\]*\]\]/,$e=(e,t)=>t.context==="table"?f(e,t):[{type:"strong",raw:e,nodes:v(e.substring(2,e.length-2),{...t,nested:!0})}],Ae=l($e,{parseOnNested:!1,parseOnQuoted:!0,patterns:[je]}),Ue=/\[\$ .+? \]/,_e=/\[\$ [^\]]+\]/,Be=(e,t)=>t.context==="table"?f(e,t):[{type:"formula",raw:e,formula:e.substring(3,e.length-(e.endsWith(" ]")?2:1))}],Fe=l(Be,{parseOnNested:!1,parseOnQuoted:!0,patterns:[Ue,_e]}),Ke=/\[[!"#%&'()*+,\-./{|}<>_~]+ (?:\[[^[\]]+\]|[^\]])+\]/,qe=(e,t)=>{if(t.context==="table")return f(e,t);let n=e.indexOf(" "),o=e.substring(1,n),r=e.substring(n+1,e.length-1),s=new Set(o);if(s.has("*")){let i=o.split("*").length-1;s.delete("*"),s.add(*-${Math.min(i,10)})}return[{type:"decoration",raw:e,rawDecos:o,decos:Array.from(s),nodes:v(r,{...t,nested:!0})}]},ze=l(qe,{parseOnNested:!1,parseOnQuoted:!0,patterns:[Ke]}),We=/.*?/,Qe=(e,t)=>t.context==="table"?f(e,t):[{type:"code",raw:e,text:e.substring(1,e.length-1)}],Xe=l(Qe,{parseOnNested:!1,parseOnQuoted:!0,patterns:[We]}),Ye=/^[$%] .+$/,Ge=(e,t)=>{var n;if(t.context==="table")return f(e,t);let o=(n=e[0])!==null&&n!==void 0?n:"",r=e.substring(2);return[{type:"commandLine",raw:e,symbol:o,text:r}]},Je=l(Ge,{parseOnNested:!1,parseOnQuoted:!1,patterns:[Ye]}),Ve=/\[\s+\]/,Ze=(e,t)=>t.context==="table"?f(e,t):[{type:"blank",raw:e,text:e.substring(1,e.length-1)}],et=l(Ze,{parseOnNested:!1,parseOnQuoted:!0,patterns:[Ve]}),tt=/\[https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)(?:\?[^\]\s]+)?(?:\s+https?:\/\/[^\s\]]+)?\]/i,nt=/\[https?:\/\/[^\s\]]+\s+https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)(?:\?[^\]\s]+)?\]/i,rt=/\[https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}(?:\/raw)?(?:\s+https?:\/\/[^\s\]]+)?\]/,ot=/\[https?:\/\/[^\s\]]+\s+https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}(?:\/raw)?\]/,st=e=>/^https?:\/\/[^\s\]]+\.(png|jpe?g|gif|svg)(\?[^\]\s]+)?$/i.test(e)||it(e),it=e=>/^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}(\/raw)?$/.test(e),at=(e,t)=>{if(t.context==="table")return f(e,t);let n=e.search(/\s/),o=n!==-1?e.substring(1,n):e.substring(1,e.length-1),r=n!==-1?e.substring(n,e.length-1).trimLeft():"",[s,i]=st(r)?[r,o]:[o,r];return[{type:"image",raw:e,src:/^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}$/.test(s)?${s}/thumb/1000:s,link:i}]},ct=l(at,{parseOnNested:!0,parseOnQuoted:!0,patterns:[tt,nt,rt,ot]}),pt=/\[https?:\/\/[^\s\]]+\s+[^\]]*[^\s]\]/,dt=/\[[^[\]]*[^\s]\s+https?:\/\/[^\s\]]+\]/,ut=/\[https?:\/\/[^\s\]]+\]/,lt=/https?:\/\/[^\s]+/,mt=(e,t)=>{if(t.context==="table")return f(e,t);let n=e.startsWith("[")&&e.endsWith("]")?e.substring(1,e.length-1):e,o=/^https?:\/\/[^\s\]]/.test(n),r=(o?/^https?:\/\/[^\s\]]+/:/https?:\/\/[^\s\]]+$/).exec(n);if((r==null?void 0:r[0])===void 0)return[];let s=o?n.substring(r[0].length):n.substring(0,r.index-1);return[{type:"link",raw:e,pathType:"absolute",href:r[0],content:s.trim()}]},ft=l(mt,{parseOnNested:!0,parseOnQuoted:!0,patterns:[pt,dt,ut,lt]}),J=/\[([^\]]*[^\s])\s+([NS]\d+(?:\.\d+)?,[EW]\d+(?:\.\d+)?(?:,Z\d+)?)\]/,V=/\[([NS]\d+(?:\.\d+)?,[EW]\d+(?:\.\d+)?(?:,Z\d+)?)(?:\s+([^\]]*[^\s]))?\]/,gt=e=>{let[t="",n="",o=""]=e.split(","),r=parseFloat(t.replace(/^N/,"").replace(/^S/,"-")),s=parseFloat(n.replace(/^E/,"").replace(/^W/,"-")),i=/^Z\d+$/.test(o)?parseInt(o.replace(/^Z/,""),10):14;return{latitude:r,longitude:s,zoom:i}},ht=(e,t)=>{var n;if(t.context==="table")return f(e,t);let o=(n=e.match(J))!==null&&n!==void 0?n:e.match(V);if(o===null)return[];let r=e.startsWith("[N")||e.startsWith("[S"),[,s="",i=""]=r?o:[o[0],o[2],o[1]],{latitude:a,longitude:p,zoom:d}=gt(s),c=i!==""?https://www.google.com/maps/place/${encodeURIComponent(i)}/@${a},${p},${d}z:https://www.google.com/maps/@${a},${p},${d}z;return[{type:"googleMap",raw:e,latitude:a,longitude:p,zoom:d,place:i,url:c}]},xt=l(ht,{parseOnNested:!1,parseOnQuoted:!0,patterns:[J,V]}),wt=/\[\/?[^[\]]+\]/,yt=e=>{let t=e.substring(1,e.length-1);return[{type:"link",raw:e,pathType:t.startsWith("/")?"root":"relative",href:t,content:""}]},Et=l(yt,{parseOnNested:!0,parseOnQuoted:!0,patterns:[wt]}),bt=/(?:^|\s)#\S+/,kt=(e,t)=>{if(t.context==="table")return f(e,t);if(e.startsWith("#"))return[{type:"hashTag",raw:e,href:e.substring(1)}];let n=e.substring(0,1),o=e.substring(1);return[...f(n,t),{type:"hashTag",raw:o,href:o.substring(1)}]},vt=l(kt,{parseOnNested:!0,parseOnQuoted:!0,patterns:[bt]}),Tt=(e,t,n)=>{var o;return e===""?[]:(o=n==null?void 0:n())!==null&&o!==void 0?o:[]},Lt=(...e)=>(t,n)=>e.reduceRight((o,r)=>()=>r(t,n,o),()=>ye(t,n))(),v=Lt(Tt,ke,Le,Xe,Je,Fe,et,ze,Ne,Se,Ae,ct,ft,Oe,xt,Et,vt),It=e=>{let{rows:[t,...n]}=e,{indent:o=0,text:r=""}=t??{},s=r.replace(/^\s*table:/,"");return{indent:o,type:"table",fileName:s,cells:n.map(i=>i.text.substring(o+1)).map(i=>i.split(" ").map(a=>v(a,{nested:!1,quoted:!1,context:"table"})))}},Mt=e=>{let{indent:t,text:n}=e.rows[0];return{indent:t,type:"line",nodes:v(n.substring(t),{nested:!1,quoted:!1,context:"line"})}},Z=e=>{switch(e.type){case"title":return xe(e);case"codeBlock":return we(e);case"table":return It(e);case"line":return Mt(e)}},ee=e=>e.split( ).map(t=>{var n,o,r;return{indent:(r=(o=(n=/^\s+/.exec(t))===null||n===void 0?void 0:n[0])===null||o===void 0?void 0:o.length)!==null&&r!==void 0?r:0,text:t}}),Ct=(e,t)=>{var n,o;return(e.type==="codeBlock"||e.type==="table")&&t.indent>((o=(n=e.rows[0])===null||n===void 0?void 0:n.indent)!==null&&o!==void 0?o:0)},te=(e,t)=>{let n=e[e.length-1];return n!==void 0&&Ct(n,t)?(n.rows.push(t),e):(e.push({type:/^\s*code:/.test(t.text)?"codeBlock":/^\s*table:/.test(t.text)?"table":"line",rows:[t]}),e)},ne=(e,t)=>{var n;if((n=t.hasTitle)!==null&&n!==void 0?n:!0){let[o,...r]=e;return o===void 0?[]:[{type:"title",rows:[o]},...r.reduce(te,[])]}return e.reduce(te,[])};function j(e,t,{userId:n,head:o}){let r=t.flatMap(c=>c.split(
)),s=[...Y(e,r,{userId:n})];(e[0].text!==r[0]||!o.persistent)&&s.push({title:r[0]});let i=e.slice(1,6).map(c=>c.text),a=r.slice(1,6);i.join("")!==a.join("")&&s.push({descriptions:a});let[p,d]=Nt(r.join(
));return(o.linksLc.length!==p.length||!o.linksLc.every(c=>p.includes(c)))&&s.push({links:p}),o.image!==d&&s.push({image:d}),s}function Nt(e){let t=ee(e),n=ne(t,{hasTitle:!0}).flatMap(i=>{switch(i.type){case"codeBlock":case"title":return[];case"line":case"table":return[Z(i)]}}),o=[],r=null,s=i=>{switch(i.type){case"hashTag":o.push(T(i.href));return;case"link":{if(i.pathType!=="relative")return;o.push(T(i.href));return}case"image":case"strongImage":{r??=i.src.endsWith("/thumb/1000")?i.src.replace(/\/thumb\/1000$/,"/raw"):i.src;return}case"strong":case"quote":case"decoration":{for(let a of i.nodes)s(a);return}default:return}};for(let i of Pt(n))s(i);return[o,r]}function*Pt(e){for(let t of e)switch(t.type){case"codeBlock":case"title":continue;case"line":for(let n of t.nodes)yield n;continue;case"table":{for(let n of t.cells)for(let o of n)for(let r of o)yield r;continue}}}async function M(e,t){let n=await A(e,t);if(!n.ok)throw new Error(You have no privilege of editing "/${e}/${t}".);let{commitId:o,persistent:r,image:s,links:i,lines:a,id:p,pin:d}=n.value;return{commitId:o,pageId:p,persistent:r,image:s,linksLc:i.map(c=>T(c)),pin:d,lines:a}}async function $(e,t,n){return t.length===0?{commitId:n.parentId}:await e("socket.io-request",{method:"commit",data:{kind:"page",...n,changes:t,cursor:null,freeze:!0}})}async function re(e,t,n){let[o,r,s]=await Promise.all([M(e,t),O(e),S()]),i=o,a=await L();try{let{request:p}=I(a);for(let d=0;d<3;d++)try{let c=n(i.lines,i),g=c instanceof Promise?await c:c,m=j(i.lines,g,{userId:s,head:i});await $(p,m,{parentId:i.commitId,projectId:r,pageId:i.pageId,userId:s});break}catch{if(d===2)throw Error("Faild to retry pushing.");console.log("Faild to push a commit. Retry after pulling new commits");try{i=await M(e,t)}catch(g){throw g}}}finally{a.disconnect()}}scrapbox.PopupMenu.addButton({title:e=>oe(e)?"update links":"",onClick:e=>{let t=oe(e);if(!t)return;let n=window.prompt(Replace "${t}" to`,t)?.replace?.(/[\\\n]/g," ")??"";if(n==="")return;let o=scrapbox.Project.name;Promise.all([_(o,t,n),re(o,t,(r,{persistent:s})=>{let i=r.map(a=>a.text);return s?n,...i.slice(1):i})])}});function oe(e){return e.match(/\[([^\!"#%&'()\*\+,\-\.\/\{\|\}<>_~[^\\]*)\]/)?.1}